package com.xiaoshuidi.cloud.module.contract.service.bill;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageParam;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.mybatis.core.util.MyBatisUtils;
import com.xiaoshuidi.cloud.framework.oss.core.OosFileService;
import com.xiaoshuidi.cloud.framework.redis.sequence.TimebaseSequenceGenerator;
import com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils;
import com.xiaoshuidi.cloud.module.bpm.api.task.BpmProcessInstanceApi;
import com.xiaoshuidi.cloud.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import com.xiaoshuidi.cloud.module.bpm.enums.BpmBusinessTypeEnum;
import com.xiaoshuidi.cloud.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.OrderDetailVo;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.OrderDetaillAndOrderFeeVo;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.OrderFeesInformationVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.OrderItemInfo;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.orderFees.OrderFeesCreateReqVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.orderFees.OrderFeesRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.orderFees.OrderFeesVo;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.rentOrder.*;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.EnergyConsumptionImportExcelVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.HomeQueryVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.company.CompanyContractListRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.company.CompanyContractRentDataRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.home.vo.HomeData;
import com.xiaoshuidi.cloud.module.contract.controller.admin.roommate.vo.ContractRoommateImportRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.app.contract.vo.AppRentOrderCreateReq;
import com.xiaoshuidi.cloud.module.contract.convert.bill.OrderFeesConvert;
import com.xiaoshuidi.cloud.module.contract.convert.bill.RentOrderConvert;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.OrderFeesDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.RentOrderDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.RentOrderEXDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.ContractRoomDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.SettlementDO;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.bill.OrderFeesMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.bill.RentOrderMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.CompanyContractMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractRoomMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.SettlementMapper;
import com.xiaoshuidi.cloud.module.contract.enums.ApiConstants;
import com.xiaoshuidi.cloud.module.contract.enums.ApproveTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.bill.*;
import com.xiaoshuidi.cloud.module.contract.enums.contract.CompanyContractOrderShareTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractStatusEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.CustomerTypeEnum;
import com.xiaoshuidi.cloud.module.contract.service.approval.ContractApprovalService;
import com.xiaoshuidi.cloud.module.contract.service.bill.dto.RentOrderQueryDTO;
import com.xiaoshuidi.cloud.module.contract.service.bill.dto.SendUrgeMessageDTO;
import com.xiaoshuidi.cloud.module.contract.service.contract.CompanyContractService;
import com.xiaoshuidi.cloud.module.contract.service.home.HomeService;
import com.xiaoshuidi.cloud.module.contract.util.bill.BillPeriod;
import com.xiaoshuidi.cloud.module.contract.util.bill.DayRent;
import com.xiaoshuidi.cloud.module.contract.util.bill.RemainingAmount;
import com.xiaoshuidi.cloud.module.contract.util.date.DateUtils;
import com.xiaoshuidi.cloud.module.contract.vo.ApproveReqVO;
import com.xiaoshuidi.cloud.module.contract.vo.GetSowTicketOrderByIdsReq;
import com.xiaoshuidi.cloud.module.contract.vo.RentOrderSowTicketVO;
import com.xiaoshuidi.cloud.module.finance.api.finance.FinanceApi;
import com.xiaoshuidi.cloud.module.finance.api.finance.dto.CarryForwardDTO;
import com.xiaoshuidi.cloud.module.finance.api.finance.dto.CheckPayResultReqDTO;
import com.xiaoshuidi.cloud.module.finance.api.finance.dto.FinanceMoneyFlowDTO;
import com.xiaoshuidi.cloud.module.finance.enums.finance.SourceBizTypeEnum;
import com.xiaoshuidi.cloud.module.iot.common.ResultModel;
import com.xiaoshuidi.cloud.module.rooms.api.apartment.ApartmentApi;
import com.xiaoshuidi.cloud.module.rooms.api.apartment.dto.ApartmentListDto;
import com.xiaoshuidi.cloud.module.rooms.api.reserve.ReserveApi;
import com.xiaoshuidi.cloud.module.rooms.api.reserve.vo.ReserveGetApiResp;
import com.xiaoshuidi.cloud.module.rooms.api.roomhouse.RoomHouseApi;
import com.xiaoshuidi.cloud.module.rooms.api.sdiot.IotRoomDeviceApi;
import com.xiaoshuidi.cloud.module.rooms.vo.RoomHouseRespVo;
import com.xiaoshuidi.cloud.module.system.api.sms.SmsSendApi;
import com.xiaoshuidi.cloud.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
import com.xiaoshuidi.cloud.module.system.api.user.AdminUserApi;
import com.xiaoshuidi.cloud.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static com.xiaoshuidi.cloud.module.contract.enums.ApiConstants.ZERO;
import static com.xiaoshuidi.cloud.module.contract.enums.ErrorCodeConstants.*;


/**
 * 账单 Service 实现类
 *
 * @author 合房租赁
 */
@Service
@Slf4j
public class RentOrderServiceImpl implements RentOrderService {
    @Resource
    private RentOrderMapper rentOrderMapper;
    @Resource
    private OrderFeesMapper orderfeesMapper;

    @Resource
    private SettlementMapper settlementMapper;
    @Resource
    private ContractRoomMapper contractRoomMapper;
    @Resource
    private TimebaseSequenceGenerator timebaseSequenceGenerator;
    @Resource
    private OrderFeesService orderFeesService;

    @Resource
    private ContractApprovalService contractApprovalService;

    @Resource
    private SmsSendApi smsSendApi;
    @Lazy
    @Resource
    private CompanyContractService companyContractService;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;

    @Value("${spring.application.name}")
    private String applicationName;
    @Resource
    private FinanceApi financeApi;

    @Resource
    private HomeService homeService;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private ReserveApi reserveApi;
    @Resource
    private IotRoomDeviceApi iotRoomDeviceApi;
    @Resource
    private OosFileService oosFileService;
    @Resource
    private CompanyContractMapper companyContractMapper;
    @Resource
    private RoomHouseApi roomHouseApi;
    @Resource
    private ApartmentApi apartmentApi;

    /*
     * 批量创建合同账单
     * */
    @Override
    public List<RentOrderDO> createBatchRentOrder(List<OrderDetailVo> billsList, RentOrderVO rentOrderVO) {
        // 个人账单期数
        int defNum = 0;
        // 企业账单期数
        int num = 0;
        Integer totalNum;
        totalNum = (OrderTypeEnum.DEFAULT.getCode().equals(rentOrderVO.getOrderType())) ? billsList.size():1;
       /* if (CompanyContractOrderShareTypeEnum.BUSINESS_BEARS_ALL_THE_COSTS.getCode().equals(rentOrderVO.getOrderShareMethod())) {
        }else {
            totalNum = (OrderTypeEnum.DEFAULT.getCode().equals(rentOrderVO.getOrderType()))?(billsList.size()/2):1;
        }*/
        List<RentOrderDO> rentOrderDOList = new ArrayList<>();
        for (OrderDetailVo item : billsList) {
            BigDecimal rentDeposit = item.getRentAmount().add(item.getDepositAmount());

            RentOrderDO rentOrderDO = RentOrderConvert.INSTANCE.convert(item);
            rentOrderDO.setUuid(UUID.randomUUID().toString(true));
            rentOrderDO.setOrderNo(nextRentOrderNo());
            if(1==item.getRoommateId()){
                defNum = defNum + 1;
                rentOrderDO.setNum(defNum);
            }
            if(0==item.getRoommateId()){
                num = num + 1;
                rentOrderDO.setNum(num);
            }
            // 预定金赋值到押金费项明细
            if (num == 1 && Objects.nonNull(rentOrderVO.getBooking())){
                for (OrderFeesCreateReqVO orderFeesCreateReqVO : rentOrderDO.getFeeDetail()) {
                    if (ApiConstants.DEPOSIT_FEE_ID == orderFeesCreateReqVO.getFeeId()){
                        orderFeesCreateReqVO.setBookingAmount(rentOrderVO.getBooking().getBookingDeposit());
                    }
                }
            }
            rentOrderDO.setTotalNum(totalNum);
            rentOrderDO.setRentDeposit(rentDeposit);
            rentOrderDO.setOughtPayTime(item.getReceivablesTime());
            rentOrderDO.setDeadlinePayTime(item.getReceivablesTime());
            rentOrderDO.setBusinessId(rentOrderVO.getContractId());
            rentOrderDO.setCustomerName(rentOrderVO.getCustomerName());
            rentOrderDO.setCustomerPhone(rentOrderVO.getCustomerPhone());
            rentOrderDO.setApartmentId(rentOrderVO.getApartmentId());
            rentOrderDO.setApartmentName(rentOrderVO.getApartmentName());
            rentOrderDO.setRoomId(rentOrderVO.getRoomId());
            rentOrderDO.setRoomName(rentOrderVO.getRoomName());
            rentOrderDO.setPhone(rentOrderVO.getCustomerPhone());
            rentOrderDO.setOrderType(rentOrderVO.getOrderType());
            rentOrderDO.setRawAmount(BigDecimal.ZERO);
            rentOrderDO.setApprovedStatus(rentOrderVO.getApprovedStatus());
            rentOrderDO.setPayOutStatus(0);
            rentOrderDO.setPaidRentUtilities(item.getPaidRentUtilities());
            rentOrderDO.setDiscountAmount(item.getDiscountValue().add(item.getExtraDiscountValue()));
            rentOrderDO.setPayStatus(item.getPayStatus());
            rentOrderDO.setRoommateId(item.getRoommateId());
            rentOrderDO.setMemberId(rentOrderVO.getMemberId());
            rentOrderDO.setOperatorName(rentOrderVO.getOperatorName());
            rentOrderDO.setRemark(rentOrderVO.getRemark());
            // 20230729 apartmentId与deptId一致
            rentOrderDO.setDeptId(rentOrderVO.getApartmentId());
            rentOrderDO.setSowTicketId(rentOrderVO.getSowTicketId());
            // 抵扣完成 实际已付时间
            if (OrderPayStatusEnum.PAID.getCode().equals(item.getPayStatus())){
                rentOrderDO.setActualPayTime(LocalDateTime.now());
            }
            rentOrderDOList.add(rentOrderDO);
        }
        log.info("======创建账单数据rentOrderDOList：{}",rentOrderDOList);
        rentOrderMapper.insertBatch(rentOrderDOList);
        // 返回
        return rentOrderDOList;
    }

    @Override
    public String nextRentOrderNo() {
        return ApiConstants.ZKHTZD + timebaseSequenceGenerator.YYYYMMDD.next(ApiConstants.BILL, "%06d");
    }

    /*
     * 获取应退账单-根据合同id
     * */
    @Override
    public List<RemainingAmount> getByContractId(Long contractId) {
        //1.获取合同下的所有账单
        LambdaQueryWrapper<RentOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RentOrderDO::getBusinessId, contractId)
                .in(RentOrderDO::getOrderType, Arrays.asList(OrderTypeEnum.DEFAULT.getCode(),
                        OrderTypeEnum.CUSTOM.getCode()));
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(wrapper);
        List<Long> orderIds = rentOrderDOList.stream().map(RentOrderDO::getId).collect(Collectors.toList());

        List<OrderFeesDO> orderFeesDOS = orderFeesService.getOrderFeeByorderIds(orderIds, Arrays.asList(OrderTypeEnum.DEFAULT.getCode()));
        if (CollectionUtil.isEmpty(orderFeesDOS)) {
            return null;
        }
        Map<Long, RentOrderDO> rentOrderDOMap = rentOrderDOList.stream().collect(Collectors.toMap(RentOrderDO::getId, v -> v));

        orderFeesDOS.forEach(item -> item.setPayStatus(rentOrderDOMap.get(item.getOrderId()).getPayStatus()));

        List<RemainingAmount> remainingAmount = RentOrderConvert.INSTANCE.convertRemainingList(orderFeesDOS);

        return DayRent.remainingAmount(remainingAmount);
    }

    /*
     * 更新账单
     * */
    @Override
    public void updateContractRentOrder(RentOrderUpdateReqVO updateReqVO) {
        // 校验存在
        validateContractRentOrderExists(updateReqVO.getId());
        // 更新
        RentOrderDO updateObj = RentOrderConvert.INSTANCE.convert(updateReqVO);
        rentOrderMapper.updateById(updateObj);
    }

    @Override
    public void updateContractRentOrderById(RentOrderUpdateReqVO updateReqVO) {
        RentOrderDO updateObj = RentOrderConvert.INSTANCE.convert(updateReqVO);
        rentOrderMapper.updateById(updateObj);
    }

    /*
     * 删除账单
     * */
    @Override
    public void deleteContractRentOrder(Long id) {
        // 校验存在
        validateContractRentOrderExists(id);
        // 删除
        rentOrderMapper.deleteById(id);
    }

    /*
     * 单个获得账单 账单id
     * */
    @Override
    public RentOrderDO getContractRentOrder(Long id) {
        return rentOrderMapper.selectById(id);
    }

    /*
     * 获得账单列表  账单id集合
     * */
    @Override
    public List<RentOrderDO> getContractRentOrderList(Collection<Long> ids) {
        return rentOrderMapper.selectBatchIds(ids);
    }

    /*
     * 租客账单待处理、未出账、已处理
     * */
    @Override
    public RentOrderInfoVO getRentOrder(Long contractId, List<String> orderTypes) {
        //获取合同下的所有账单
        LambdaQueryWrapper<RentOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RentOrderDO::getBusinessId, contractId)
                .ne(RentOrderDO::getApprovedStatus, OrderApprovedStatusEnum.REJECT.getCode())
                .in(CollectionUtils.isNotEmpty(orderTypes), RentOrderDO::getOrderType, orderTypes);
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(wrapper);

        return buildRentOrderInfoVO(rentOrderDOList);
    }

    // 组装账单数据
    private RentOrderInfoVO buildRentOrderInfoVO(List<RentOrderDO> rentOrderDOList) {
        // 检查支付金额, 防止支付结果没有回调
        checkPaidResult(rentOrderDOList);

        //获取已支付和已取消
        List<RentOrderDO> paids = rentOrderDOList.stream().filter(p -> (OrderPayStatusEnum.PAID.getCode().equals(p.getPayStatus())
                || OrderPayStatusEnum.CANCEL.getCode().equals(p.getPayStatus()))).collect(Collectors.toList());

        //获取未支付，未逾期的房租账单
        List<RentOrderDO> outStandings = rentOrderDOList.stream().filter(p ->
                OrderPayStatusEnum.UNPAID.getCode().equals(p.getPayStatus()) &&
                        p.getOughtPayTime().isAfter(LocalDate.now())
        ).collect(Collectors.toList());

        //合同下的账单除去已支付账单和未出账单得到未处理账单
        rentOrderDOList.removeAll(paids);
        rentOrderDOList.removeAll(outStandings);

        //未处理账单
        List<RentOrderDO> unpaids = rentOrderDOList;

        //3.1 从outStandings找到最小的应支付日期的账单
        Optional<RentOrderDO> min = outStandings.stream().min(Comparator.comparing(RentOrderDO::getOughtPayTime));
        boolean flag = false;//未出账的账单中需要推一条最小日期到未处理订单
        if (min.isPresent()) {
            RentOrderDO minto = min.get();//未出账期里日期最小的
            //从待处理的里面账单属于合同账单，且日期是最大的
            Optional<RentOrderDO> max = unpaids.stream()
                    .filter(p -> (p.getOrderType().equals(OrderTypeEnum.DEFAULT.getCode()) || p.getOrderType().equals(OrderTypeEnum.OWNER.getCode())))
                    .max(Comparator.comparing(RentOrderDO::getOughtPayTime));
            if (max.isPresent()) {
                RentOrderDO maxto = max.get();
                //max存在，判断这条的应收日期是否在min之前或者重合，说明不需要推
                if (maxto.getOughtPayTime().isAfter(minto.getOughtPayTime())) {
                    flag = true;
                }
            } else {
                //待处理账单中没有房租类型的账单，需要推一条上去
                flag = true;
            }
        }
        if (flag) {
            outStandings = outStandings.stream().filter(item -> item != min.get()).collect(Collectors.toList());
            unpaids.add(min.get());
        }

        //转换集合返给前端
        //待处理
        List<OrderItemInfo> unPaidBills = convertBill(unpaids, OrderStatusEnum.RECEIVABLE.getCode());
        //已处理
        dealPaids(paids);
        List<OrderItemInfo> paidBills = convertBill(paids, OrderStatusEnum.PAID.getCode());
        List<OrderItemInfo> outStandingBills = convertBill(outStandings, OrderStatusEnum.UNPAID.getCode());
        RentOrderInfoVO rentOrderInfoVo = new RentOrderInfoVO();
        rentOrderInfoVo.setUnPaidBills(unPaidBills);
        rentOrderInfoVo.setPaidBills(paidBills);
        rentOrderInfoVo.setOutStandingBills(outStandingBills);
        return rentOrderInfoVo;
    }

    private void dealPaids(List<RentOrderDO> paids){
        if (CollectionUtils.isNotEmpty(paids)){
            for (RentOrderDO paid : paids) {
                if (BigDecimal.ZERO.compareTo(Objects.nonNull(paid.getActualPaymentAmount())?paid.getActualPaymentAmount():BigDecimal.ZERO) == 0){
                    // 无实付金额  已付总额-优惠金额-优惠券优惠金额
                    BigDecimal paidRentUtilities = Objects.nonNull(paid.getPaidRentUtilities()) ? paid.getPaidRentUtilities() : BigDecimal.ZERO;
                    BigDecimal discountAmount = BigDecimal.ZERO;
                    if (!OrderTypeEnum.OWNER.getCode().equals(paid.getOrderType())){
                        // 业主合同的免租期放在discountAmount里，但不会在
                        discountAmount = Objects.nonNull(paid.getDiscountAmount()) ? paid.getDiscountAmount() : BigDecimal.ZERO;
                    }
                    BigDecimal couponDiscount = Objects.nonNull(paid.getCouponDiscount()) ? paid.getCouponDiscount() : BigDecimal.ZERO;
                    paid.setActualPaymentAmount((paidRentUtilities.subtract(discountAmount)).subtract(couponDiscount));
                }
            }
        }
    }

    /*
     * 获得某日期下的应收账单
     * */
    @Deprecated
    @Override
    public RentOrderHomeRespVO getReceivableOrders(HomeQueryVO req) {
        IPage<T> mpPage = MyBatisUtils.buildPage(req);
        LocalDate receivableDate = req.getStartDate();
        IPage<OrderItemInfo> records = rentOrderMapper.getReceivableBills(mpPage, receivableDate,req.getApartmentId());
        List<OrderItemInfo> receivableBills = records.getRecords();
        receivableBills.forEach(b -> {
            if (b.getOughtPayTime().isBefore(LocalDate.now())) {
                long days = ChronoUnit.DAYS.between(b.getOughtPayTime(), LocalDate.now());
                b.setBillTypeName("逾期" + days + "天");
            } else if (ChronoUnit.DAYS.between(LocalDate.now(), b.getOughtPayTime()) <= 10 &&
                    ChronoUnit.DAYS.between(LocalDate.now(), b.getOughtPayTime()) >= 0) {
                b.setBillTypeName(OrderStatusEnum.RECEIVABLE.getMsg());
            } else {
                b.setBillTypeName(OrderStatusEnum.UNPAID.getMsg());
            }
        });
        PageResult<OrderItemInfo> result = new PageResult<>();
        result.setTotal(records.getTotal());
        result.setList(receivableBills);

        HomeData homeData = homeService.getHomeData(req);
        RentOrderHomeRespVO rentOrderHomeRespVO = new RentOrderHomeRespVO();
        rentOrderHomeRespVO.setOrderItemInfo(result);
        rentOrderHomeRespVO.setHomeData(homeData);

        return rentOrderHomeRespVO;
    }

    public List<RentOrderDO> createRentOrder(RentOrderCreateReq req){
        return this.createRentOrder(req, true);
    }

    /*
     * 创建杂费账单
     * */
    @Transactional(rollbackFor = Exception.class)
    public List<RentOrderDO> createRentOrder(RentOrderCreateReq req,Boolean flag) {
        LocalDate startTime = req.getStartTime();
        LocalDate endTime = req.getEndTime();
        LocalDate oughtPayTime = req.getOughtPayTime();
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(req.getContractId());
        String mark = contractApprovalService.getByRoomIdAndMark(contractRoomDO.getApartmentId(), ApproveTypeEnum.ORDER.getMsg());
        String approvedStatus;
        if (StringUtils.isNotBlank(mark)&& flag) {
            approvedStatus = OrderApprovedStatusEnum.IN_REVIEW.getCode();
        } else {
            approvedStatus = OrderApprovedStatusEnum.NO_AUDIT_REQUITED.getCode();
        }
        Integer roommateId = 0;
        if (CustomerTypeEnum.COMPANY.getCode().equals(contractRoomDO.getCustomerType())
                && CompanyContractOrderShareTypeEnum.ALL.getCode().equals(contractRoomDO.getOrderShareMethod())) {
            roommateId = 1;
        }
        req.setMemberId(roommateId==0?contractRoomDO.getMemberId():null);
        //账单生成
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();

        for (OrderFeesVo item : req.getOrderFeesVo()) {
            BigDecimal discountValue = item.getDiscountValue() == null ? BigDecimal.ZERO : item.getDiscountValue();
            //如果是一次性费用直接创建账单
            if (OrderFeeSortTypeEnum.ONCE.getCode().equals(item.getFeeSort())||OrderFeeSortTypeEnum.DEPOSIT.getCode().equals(item.getFeeSort())) {
                List<OrderFeesCreateReqVO> feeDetail = new ArrayList<>();
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                OrderFeesCreateReqVO orderFeesCreateReqVO = getOrderFeesCreateReqVO(item, item.getFeeAmount(), startTime, endTime, discountValue, req.getOrderType());
                orderFeesCreateReqVO.setDiscountAmount(discountValue);
                feeDetail.add(orderFeesCreateReqVO);
                orderDetailVo.setStartTime(startTime);
                orderDetailVo.setEndTime(endTime);
                orderDetailVo.setDepositAmount(BigDecimal.ZERO);
                orderDetailVo.setTotalAmount(item.getFeeAmount().subtract(discountValue));
                orderDetailVo.setExtrasAmount(item.getFeeAmount());
                orderDetailVo.setReceivablesTime(oughtPayTime);
                orderDetailVo.setApprovedStatus(approvedStatus);
                orderDetailVo.setFeeDetail(feeDetail);
                orderDetailVo.setRoommateId(roommateId);
                orderDetailVoList.add(orderDetailVo);
            }
            if (OrderFeeSortTypeEnum.CYCLE.getCode().equals(item.getFeeSort())) {
                //付款周期
                Integer payMethodF = item.getPaymentCycle();
                List<BillPeriod> billPeriods = BillPeriod.splitByMonth(startTime, endTime, payMethodF);
                for (BillPeriod billPeriod : billPeriods) {
                    List<OrderFeesCreateReqVO> feeDetail = new ArrayList<>();
                    OrderDetailVo orderDetailVo = new OrderDetailVo();
                    LocalDate billStartDate = billPeriod.getStartTime();
                    //计算日租金
                    BigDecimal totalAmount = billPeriod.totalFee(item.getFeeAmount());
                    //计算日优惠
                    BigDecimal totalDisAmount = billPeriod.totalFee(discountValue);
                    OrderFeesCreateReqVO orderFeesCreateReqVO = getOrderFeesCreateReqVO(item, totalAmount, billPeriod.getStartTime(),
                            billPeriod.getEndTime(), totalDisAmount, req.getOrderType());
                    orderFeesCreateReqVO.setDiscountAmount(totalDisAmount);
                    feeDetail.add(orderFeesCreateReqVO);
                    orderDetailVo.setStartTime(billPeriod.getStartTime());
                    orderDetailVo.setEndTime(billPeriod.getEndTime());
                    orderDetailVo.setDepositAmount(BigDecimal.ZERO);
                    orderDetailVo.setTotalAmount(totalAmount.subtract(totalDisAmount));
                    orderDetailVo.setExtrasAmount(totalAmount);
                    int months = Period.between(startTime, billStartDate).getMonths();

                    orderDetailVo.setReceivablesTime(oughtPayTime.plusMonths(months));
                    orderDetailVo.setApprovedStatus(approvedStatus);
                    orderDetailVo.setFeeDetail(feeDetail);
                    orderDetailVo.setRoommateId(roommateId);

                    orderDetailVoList.add(orderDetailVo);
                }
            }
        }
        log.info("======账单生成数据orderDetailVoList：{}",orderDetailVoList);
        RentOrderVO rentOrderVO = RentOrderConvert.INSTANCE.convert(req);
        rentOrderVO.setCustomerName(contractRoomDO.getCustomerName());
        rentOrderVO.setCustomerPhone(contractRoomDO.getCustomerPhone());
        rentOrderVO.setApartmentId(contractRoomDO.getApartmentId());
        rentOrderVO.setApartmentName(contractRoomDO.getApartmentName());
        rentOrderVO.setRoomId(contractRoomDO.getRoomId());
        rentOrderVO.setRoomName(contractRoomDO.getRoomName());
        rentOrderVO.setOrderType(req.getOrderType());
        rentOrderVO.setCustomerPhone(contractRoomDO.getCustomerPhone());
        rentOrderVO.setApprovedStatus(approvedStatus);
        if (req.getOrderType().equals(OrderTypeEnum.EXTENSION.getCode())) {
            CommonResult<AdminUserRespDTO> result = adminUserApi.getUser(Long.valueOf(contractRoomDO.getCreator() == null ? "0" : contractRoomDO.getCreator()) );
            AdminUserRespDTO checkedData = result.getCheckedData();
            rentOrderVO.setOperatorName(checkedData.getNickname());
        }else {
            rentOrderVO.setOperatorName(SecurityFrameworkUtils.getLoginUser().getNickname());

        }
        log.info("======账单生成数据rentOrderVO：{}",orderDetailVoList);
        //创建账单明细
        List<RentOrderDO> rentOrder = createBatchRentOrder(orderDetailVoList, rentOrderVO);
        for (RentOrderDO rentOrderDO : rentOrder) {
            if (OrderApprovedStatusEnum.IN_REVIEW.getCode().equals(approvedStatus)) {
                Long userId = req.getOrderType().equals(OrderTypeEnum.EXTENSION.getCode()) ?
                        Long.valueOf(contractRoomDO.getCreator() == null ? "0" : contractRoomDO.getCreator()) : getLoginUser().getId();
                String processInstance = this.createProcessInstance(mark, rentOrderDO, userId);
                rentOrderDO.setProcessInstance(processInstance);
                rentOrderMapper.updateById(rentOrderDO);
            }
        }
        log.info("======创建账单明细rentOrder：{}",rentOrder);
        //创建账单明细
        orderFeesService.createBatchOrderFees(rentOrder);
        return rentOrder;
    }

    private String createProcessInstance(String mark, RentOrderDO rentOrderDO, Long userId) {
        // 工作流参数
        BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        bpmProcessInstanceCreateReqDTO.setProcessDefinitionKey(mark);
        bpmProcessInstanceCreateReqDTO.setBusinessType(BpmBusinessTypeEnum.RENT_ORDER.name());
        bpmProcessInstanceCreateReqDTO.setBusinessKey(String.valueOf(rentOrderDO.getId()));
        Map<String, Object> variables = new HashMap<>();
        variables.put("customerName", rentOrderDO.getCustomerName());
        variables.put("customerPhone", rentOrderDO.getCustomerPhone());
        variables.put("roomName", rentOrderDO.getApartmentName() + rentOrderDO.getRoomName());
        variables.put("roomId", rentOrderDO.getRoomId());
        variables.put("createName", rentOrderDO.getOperatorName());
        variables.put("orderStartDate", rentOrderDO.getStartTime().format(DateTimeFormatter.ISO_DATE));
        variables.put("orderEndDate", rentOrderDO.getEndTime().format(DateTimeFormatter.ISO_DATE));
        bpmProcessInstanceCreateReqDTO.setVariables(variables);
        bpmProcessInstanceCreateReqDTO.setTenantId(getLoginUser().getTenantId());
        bpmProcessInstanceCreateReqDTO.setServiceName(applicationName);

        if (Objects.nonNull(rentOrderDO.getBusinessId())) {
            ContractRoomDO contractRoomDO = contractRoomMapper.selectById(rentOrderDO.getBusinessId());
            if (Objects.nonNull(contractRoomDO)){
                BpmProcessInstanceCreateReqDTO.ExtendDto extendDto = new BpmProcessInstanceCreateReqDTO.ExtendDto();
                extendDto.setRoomId(Objects.nonNull(contractRoomDO.getRoomId())?contractRoomDO.getRoomId().toString():"");
                extendDto.setContractId(contractRoomDO.getId());
                extendDto.setContractNo(contractRoomDO.getContractNo());
                extendDto.setOwnerContractId(null);
                extendDto.setRoomName(contractRoomDO.getRoomName());
                extendDto.setApartmentId(contractRoomDO.getApartmentId());
                extendDto.setApartmentName(contractRoomDO.getApartmentName());
                extendDto.setCustomName(contractRoomDO.getCustomerName());
                extendDto.setCustomPhone(contractRoomDO.getCustomerPhone());
                bpmProcessInstanceCreateReqDTO.setExtendDto(extendDto);
            }
        }

        log.info("processInstance userId {}, request : {}", userId, JsonUtils.toJsonString(bpmProcessInstanceCreateReqDTO));
        CommonResult<String> processInstance = bpmProcessInstanceApi.createProcessInstance(userId, bpmProcessInstanceCreateReqDTO);
        log.info("processInstance:{}", JsonUtils.toJsonString(processInstance));
        return processInstance.getCheckedData();

    }

    /*
     * 组装账单数据
     * */
    @NotNull
    private static OrderFeesCreateReqVO getOrderFeesCreateReqVO(OrderFeesVo item, BigDecimal totalAmount, LocalDate billPeriod, LocalDate billPeriod1, BigDecimal discountAmount, String orderType) {
        OrderFeesCreateReqVO orderFeesCreateReqVO = new OrderFeesCreateReqVO();
        orderFeesCreateReqVO.setOrderType(orderType);
        orderFeesCreateReqVO.setFeeSort(item.getFeeSort());
        orderFeesCreateReqVO.setFeeId(item.getFeeId());
        orderFeesCreateReqVO.setFeeName(item.getFeeName());
        orderFeesCreateReqVO.setFlowType(0);
        orderFeesCreateReqVO.setPayStatus(OrderPayStatusEnum.UNPAID.getCode());
        orderFeesCreateReqVO.setAmount(totalAmount);
        orderFeesCreateReqVO.setDiscountAmount(discountAmount);
        orderFeesCreateReqVO.setPaidAmount(BigDecimal.ZERO);
        orderFeesCreateReqVO.setFeeMonth(item.getFeeAmount());
        orderFeesCreateReqVO.setStartTime(billPeriod);
        orderFeesCreateReqVO.setEndTime(billPeriod1);
        return orderFeesCreateReqVO;
    }

    /*
     * 数据转换
     * */
    private List<OrderItemInfo> convertBill(List<RentOrderDO> bills, String type) {
        return bills.stream().map(b -> {
        /*    OrderItemInfo.OrderItemInfoBuilder infoBuilder = OrderItemInfo.builder();
            RentOrderConvert.INSTANCE.convert(b, infoBuilder);*/

            OrderItemInfo orderItemInfo = RentOrderConvert.INSTANCE.convertItem(b);
            orderItemInfo.setReceivableAmount(orderItemInfo.getTotalAmount()
                    .subtract(b.getCouponDiscount() != null ? b.getCouponDiscount() : BigDecimal.ZERO)
                    .subtract(orderItemInfo.getPaidRentUtilities()));
            orderItemInfo.setOrderType(b.getOrderType());
            if (OrderStatusEnum.RECEIVABLE.getCode().equals(type)) {
                //如果逾期需要算出逾期天数
                if (b.getOughtPayTime().isBefore(LocalDate.now())) {
                    long days = ChronoUnit.DAYS.between(b.getOughtPayTime(), LocalDate.now());
                    orderItemInfo.setBillTypeName("逾期" + days + "天");
                } else {
                    orderItemInfo.setBillTypeName(OrderStatusEnum.RECEIVABLE.getMsg());
                }
            } else if (OrderStatusEnum.PAID.getCode().equals(type)) {
                if (OrderStatusEnum.CANCEL.getCode().equals(b.getPayStatus())) {
                    orderItemInfo.setBillTypeName(OrderStatusEnum.CANCEL.getMsg());
                } else {
                    orderItemInfo.setBillTypeName(OrderStatusEnum.PAID.getMsg());
                }
            } else if (OrderStatusEnum.UNPAID.getCode().equals(type)) {
                orderItemInfo.setBillTypeName(OrderStatusEnum.UNPAID.getMsg());
            }
            return orderItemInfo;
        }).sorted(Comparator.comparing(OrderItemInfo::getStartTime))
                .collect(Collectors.toList());
    }

    /*
     * 获取租客账单列表分页
     * */
    @Override
    public Page<RentOrderResp> getRentOrder(RentOrderQueryVo rentOrderQueryVo) {
        Long tenantId = Objects.requireNonNull(SecurityFrameworkUtils.getLoginUser()).getTenantId();
        IPage<T> mpPage = MyBatisUtils.buildPage(rentOrderQueryVo);
        IPage<RentOrderEXDO> rentOrder = rentOrderMapper.getRentOrder(mpPage, rentOrderQueryVo, tenantId);
        List<RentOrderResp> rentOrderRespVO = RentOrderConvert.INSTANCE.convertList01(rentOrder.getRecords());
        for (RentOrderResp item : rentOrderRespVO) {
            if (StringUtils.isBlank(rentOrderQueryVo.getOrderStatus())) {
                setOrderStatus(item);
            } else {
                item.setOrderStatus(rentOrderQueryVo.getOrderStatus());
                String msg = OrderStatusEnum.getEnum(rentOrderQueryVo.getOrderStatus()).getMsg();
                item.setOrderStatusName(msg);
                if (OrderStatusEnum.OVERDUE.getCode().equals(rentOrderQueryVo.getOrderStatus())) {
                    long days = ChronoUnit.DAYS.between(item.getOughtPayTime(), LocalDate.now());
                    item.setBillTypeName("已逾期" + days + "天");
                }
            }
        }
        //已付账单核对
//        verificationPaidAmount(rentOrderRespVO);
        Page<RentOrderResp> respVOPage = new Page<>();
        respVOPage.setRecords(rentOrderRespVO);
        respVOPage.setTotal(rentOrder.getTotal());
        respVOPage.setCurrent(rentOrder.getCurrent());
        return respVOPage;
    }

    @Override
    public PageResult<RentOrderResp> getRentOrderPage(RentOrderQueryVo rentOrderQueryVo) {
        Long tenantId = Objects.requireNonNull(SecurityFrameworkUtils.getLoginUser()).getTenantId();
        IPage<T> mpPage = MyBatisUtils.buildPage(rentOrderQueryVo);
        IPage<RentOrderEXDO> rentOrder = rentOrderMapper.getRentOrder(mpPage, rentOrderQueryVo, tenantId);
        List<RentOrderResp> rentOrderRespVO = RentOrderConvert.INSTANCE.convertList01(rentOrder.getRecords());
        for (RentOrderResp item : rentOrderRespVO) {
            if (StringUtils.isBlank(rentOrderQueryVo.getOrderStatus())) {
                setOrderStatus(item);
            } else {
                item.setOrderStatus(rentOrderQueryVo.getOrderStatus());
                String msg = OrderStatusEnum.getEnum(rentOrderQueryVo.getOrderStatus()).getMsg();
                item.setOrderStatusName(msg);
                if (OrderStatusEnum.OVERDUE.getCode().equals(rentOrderQueryVo.getOrderStatus())) {
                    long days = ChronoUnit.DAYS.between(item.getOughtPayTime(), LocalDate.now());
                    item.setBillTypeName("已逾期" + days + "天");
                }
            }
            if(StringUtils.isNoneBlank(item.getOrderType())){
                if(item.getOrderType().equals(OrderTypeEnum.DEFAULT.getCode())){
                    item.setOrderType(OrderTypeEnum.DEFAULT.getName());
                }else if(item.getOrderType().equals(OrderTypeEnum.CUSTOM.getCode())){
                    item.setOrderType(OrderTypeEnum.CUSTOM.getName());
                }else if(item.getOrderType().equals(OrderTypeEnum.FREE.getCode())){
                    item.setOrderType(OrderTypeEnum.FREE.getName());
                }else if(item.getOrderType().equals(OrderTypeEnum.METER.getCode())){
                    item.setOrderType(OrderTypeEnum.METER.getName());
                }else if(item.getOrderType().equals(OrderTypeEnum.CARRY.getCode())){
                    item.setOrderType(OrderTypeEnum.CARRY.getName());
                }else if(item.getOrderType().equals(OrderTypeEnum.OWNER.getCode())){
                    item.setOrderType(OrderTypeEnum.OWNER.getName());
                }else if(item.getOrderType().equals(OrderTypeEnum.EXTENSION.getCode())){
                    item.setOrderType(OrderTypeEnum.EXTENSION.getName());
                }else if(item.getOrderType().equals(OrderTypeEnum.OWNER_EVICTION.getCode())){
                    item.setOrderType(OrderTypeEnum.OWNER_EVICTION.getName());
                }else if(item.getOrderType().equals(OrderTypeEnum.COMPANY_BATCH_RENT.getCode())){
                    item.setOrderType(OrderTypeEnum.COMPANY_BATCH_RENT.getName());
                }
            }
        }
//        verificationPaidAmount(rentOrderRespVO);

        return new PageResult<>(rentOrderRespVO, rentOrder.getTotal());
    }

    private void verificationPaidAmount(List<RentOrderResp> rentOrderRespVO) {
        if (CollectionUtil.isNotEmpty(rentOrderRespVO)) {
            //非第一期账单额外处理 或无上一份合同 或非房租账单
            rentOrderRespVO.stream().filter(f ->
                    f.getNum() != 1L || ObjectUtil.isNull(f.getFromId()) || !OrderTypeEnum.DEFAULT.getCode().equals(f.getOrderType())
            ).forEach(f -> {
                f.setPaidAmount(f.getPaidRentUtilities());
            });
            //未支付单独处理
            rentOrderRespVO.stream().filter(f ->
                    !OrderPayStatusEnum.PAID.getCode().equals(f.getPayStatus())
            ).forEach(f -> {
                f.setPaidAmount(BigDecimal.ZERO);
            });

            //额外处理第一期账单

            //已支付id
            List<CarryForwardDTO> paymentOnline =
                    rentOrderRespVO.stream().filter(f -> OrderPayStatusEnum.PAID.getCode().equals(f.getPayStatus()) && f.getNum() == 1L
                    ).map(m -> {
                        CarryForwardDTO carryForwardDTO = new CarryForwardDTO();
                        carryForwardDTO.setSourceBizId(m.getId().toString());
                        return carryForwardDTO;
                    }).collect(Collectors.toList());
            CommonResult<List<FinanceMoneyFlowDTO>> listCommonResult =
                    financeApi.moneyFlowList(SourceBizTypeEnum.RENT_ORDER.name(), paymentOnline);
            if (listCommonResult.isSuccess() && CollectionUtil.isNotEmpty(listCommonResult.getData())) {
                List<FinanceMoneyFlowDTO> data = listCommonResult.getData();
                //线上支付对账
                rentOrderRespVO.forEach(f -> {
                    data.stream().filter(fb -> f.getId().toString().equals(fb.getSourceBizId())).findFirst().ifPresent(ifp -> {
                        f.setPaidAmount(ifp.getTradeAmount().subtract(ifp.getDiscount()));
                    });
                });
            }
            //第一期账单并且未对账数据(勾核或者迁移数据)
            List<RentOrderResp> collect =
                    rentOrderRespVO.stream().filter(f -> OrderPayStatusEnum.PAID.getCode().equals(f.getPayStatus())
                            && ObjectUtil.isNull(f.getPaidAmount()) && f.getNum() == 1L
                    ).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(collect)) {
                //存在上一份合同
                List<Long> ids = collect.stream().filter(ft -> ObjectUtil.isNotNull(ft.getFromId()))
                        .map(RentOrderResp::getBusinessId).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(ids)) {
                    //获取合同信息
                    List<ContractRoomDO> contractRoomDOList = contractRoomMapper.selectBatchIds(ids);

                    if (CollectionUtils.isNotEmpty(contractRoomDOList)) {
                        //获取上一份合同
                        List<Long> formIds = collect.stream().filter(ft -> ObjectUtil.isNotNull(ft.getFromId()))
                                .map(RentOrderResp::getFromId).collect(Collectors.toList());
                        //有上一份合同的
                        List<ContractRoomDO> contractRoomDOListFrom = contractRoomMapper.selectBatchIds(formIds);
                        //获取换租单
                        List<SettlementDO> settlementDOList = settlementMapper.selectList(SettlementDO::getContractId
                                , formIds);

                        if (CollectionUtil.isNotEmpty(contractRoomDOListFrom)) {
                            collect.stream().filter(ft -> ObjectUtil.isNotNull(ft.getFromId())).forEach(rm -> {
                                contractRoomDOList.stream().filter(f ->
                                        f.getId().equals(rm.getBusinessId())
                                ).findFirst().ifPresent(f -> {
                                    //查询上一份合同续租
                                    contractRoomDOListFrom.stream().filter(ff -> ff.getId().equals(f.getFromId()) && ff.getRoomId().equals(f.getRoomId()))
                                            .findFirst().ifPresent(yf -> {
                                                //租金减上一份押金
                                                rm.setPaidAmount(rm.getPaidRentUtilities().subtract(yf.getDeposit()));
                                            });
                                    //没有换组单不处理换组
                                    if (CollectionUtil.isNotEmpty(settlementDOList)) {
                                        //查询上一份合同 换租
                                        contractRoomDOListFrom.stream().filter(ff -> ff.getId().equals(f.getFromId()) && !ff.getRoomId().equals(f.getRoomId()))
                                                .findFirst().ifPresent(yf -> {
                                                    settlementDOList.stream().filter(hf -> hf.getContractId().equals(yf.getId())).findFirst()
                                                            .ifPresent(hff -> {
                                                                if ("in".equals(hff.getFlowType())) {
                                                                    //换房结转
                                                                    rm.setPaidAmount(hff.getAmount());
                                                                } else {
                                                                    //流出为第一期账单全部支付
                                                                    rm.setPaidAmount(BigDecimal.ZERO);
                                                                }
                                                            });
                                                });
                                    }

                                });
                            });
                        }
                    }
                }

            }

        }
    }

    /*
     * 查询合同账单
     * */
    @Override
    public List<RentOrderDO> getRentOrderByContractId(Long contractId) {
        return rentOrderMapper.selectList(RentOrderDO::getBusinessId, contractId);
    }

    /*
     * 设置账单状态
     * */
    private static void setOrderStatus(RentOrderResp item) {
        //最晚支付时间
        String payStatus = item.getPayStatus();

        if (OrderApprovedStatusEnum.IN_REVIEW.getCode().equals(item.getApprovedStatus())) {
            item.setOrderStatus(OrderStatusEnum.IN_REVIEW.getCode());
            item.setOrderStatusName(OrderStatusEnum.IN_REVIEW.getMsg());
            item.setBillTypeName(OrderStatusEnum.IN_REVIEW.getMsg());
            return;
        }
        if (OrderApprovedStatusEnum.REJECT.getCode().equals(item.getApprovedStatus())) {
            item.setOrderStatus(OrderStatusEnum.REJECT.getCode());
            item.setOrderStatusName(OrderStatusEnum.REJECT.getMsg());
            item.setBillTypeName(OrderStatusEnum.REJECT.getMsg());

            return;
        }

        if (OrderPayStatusEnum.CANCEL.getCode().equals(payStatus)) {
            item.setOrderStatus(OrderStatusEnum.CANCEL.getCode());
            item.setOrderStatusName(OrderStatusEnum.CANCEL.getMsg());
            item.setBillTypeName(OrderStatusEnum.CANCEL.getMsg());
            return;
        }
        if (OrderPayStatusEnum.PAID.getCode().equals(payStatus)) {
            item.setOrderStatus(OrderStatusEnum.PAID.getCode());
            item.setOrderStatusName(OrderStatusEnum.PAID.getMsg());
            item.setBillTypeName(OrderStatusEnum.PAID.getMsg());
            return;
        }
        if (OrderPayStatusEnum.PROCESS.getCode().equals(payStatus)) {
            item.setOrderStatus(OrderStatusEnum.PROCESS.getCode());
            item.setOrderStatusName(OrderStatusEnum.PROCESS.getMsg());
            item.setBillTypeName(OrderStatusEnum.PROCESS.getMsg());
            return;
        }
        //已逾期
        if (item.getDeadlinePayTime().isBefore(LocalDate.now())) {
            item.setOrderStatus(OrderStatusEnum.OVERDUE.getCode());
            item.setOrderStatusName(OrderStatusEnum.OVERDUE.getMsg());
            long days = ChronoUnit.DAYS.between(item.getOughtPayTime(), LocalDate.now());
            item.setBillTypeName("已逾期" + days + "天");
            return;
        }
        long days = LocalDate.now().until(item.getOughtPayTime(), ChronoUnit.DAYS);
        // 应处理 离应收款日小于10天
        if (0 <= days && days<10) {
            item.setOrderStatus(OrderStatusEnum.RECEIVABLE.getCode());
            item.setOrderStatusName(OrderStatusEnum.RECEIVABLE.getMsg());
            item.setBillTypeName(OrderStatusEnum.RECEIVABLE.getMsg());
            return;
        }
        //待处理 离应收款日将大于10天
        if (days >= 10) {
            item.setOrderStatus(OrderStatusEnum.UNPAID.getCode());
            item.setOrderStatusName(OrderStatusEnum.UNPAID.getMsg());
            item.setBillTypeName(OrderStatusEnum.UNPAID.getMsg());
        }
    }


    /*
     * 获取合同账单
     * */
    @Override
    public List<RentOrderRespVO> getOrderByContractId(Long id) {
        LambdaQueryWrapper<RentOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RentOrderDO::getBusinessId, id);
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(wrapper);
        return RentOrderConvert.INSTANCE.convertList(rentOrderDOList);
    }


    /*
     * 删除 业主账单
     * */
    @Override
    public void deleteOrderByBusinessId(Long businessId, String orderType) {
        if (Objects.isNull(businessId)) {
            return;
        }
        LambdaQueryWrapper<RentOrderDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RentOrderDO::getBusinessId, businessId);
        queryWrapper.eq(Objects.nonNull(orderType), RentOrderDO::getOrderType, orderType);
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(rentOrderDOList)) {
            List<Long> ids = rentOrderDOList.stream().map(RentOrderDO::getId).collect(Collectors.toList());
            LambdaQueryWrapper<OrderFeesDO> feeQueryWrapper = new LambdaQueryWrapper<>();
            feeQueryWrapper.in(OrderFeesDO::getOrderId, ids);
            orderfeesMapper.delete(feeQueryWrapper);
            rentOrderMapper.deleteBatchIds(ids);
        }
    }

    /*
     * 查找租客端的 账单
     * */
    public List<OrderDetaillAndOrderFeeVo> getOrder(Long loginUserId, String payStatus) {
        log.info("loginUserId:{},payStatus{}", loginUserId, payStatus);
        //账单
        List<OrderDetaillAndOrderFeeVo> rentOrderRespVOList = rentOrderMapper.getOrder(loginUserId, payStatus);
        List<Long> orderList = rentOrderRespVOList.stream().map(OrderDetaillAndOrderFeeVo::getId).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(orderList)) {
            return null;
        }
        LambdaQueryWrapper<OrderFeesDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(OrderFeesDO::getOrderId, orderList);
        //账单明细
        List<OrderFeesDO> orderFeesDOS = orderfeesMapper.selectList(queryWrapper);
        Map<Long, List<OrderFeesDO>> feeMaps = orderFeesDOS.stream().collect(Collectors.groupingBy(OrderFeesDO::getOrderId));

        rentOrderRespVOList.forEach(item -> {
            List<OrderFeesDO> l = feeMaps.get(item.getId());
            BigDecimal rental = item.getRentAmount();
            // 6.0系统流出费项
            BigDecimal extrasAmount = l.stream().filter(f -> f.getFlowType() == 1)
                    .map(OrderFeesDO::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal paidRental = l.stream().filter(f -> ApiConstants.RENT_FEE_ID.equals(f.getFeeId()))
                    .map(OrderFeesDO::getPaidAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            rental = rental.subtract(extrasAmount).subtract(item.getCouponDiscount() == null ? BigDecimal.ZERO : item.getCouponDiscount());
            if (paidRental.compareTo(rental) < 0) {
                rental = rental.subtract(paidRental);
            } else {
                rental = BigDecimal.ZERO;
            }
            item.setRentAmount(rental);
            List<OrderFeesRespVO> orderFeesRespVOS = OrderFeesConvert.INSTANCE.convertList(l);
            item.setOrderFeesList(orderFeesRespVOS);
        });
        return rentOrderRespVOList;
    }

    @Override
    public RentOrderCountVO getContractOrderCount(Long contractId) {
        RentOrderCountVO count = rentOrderMapper.getContractOrderCount(contractId);
        if (count!=null) {
            //查询合同
            ContractRoomDO roomContractById = contractRoomMapper.getRoomContractById(contractId);
            //免租费用
            if(roomContractById != null)
                count.setRentFreeAmount(roomContractById.getTotalFreeOfRent());
            BigDecimal rentAmount = count.getRentAmount()==null?BigDecimal.ZERO:count.getRentAmount();
            BigDecimal depositAmount = count.getDepositAmount()==null?BigDecimal.ZERO:count.getDepositAmount();
            BigDecimal extrasAmount = count.getExtrasAmount()==null?BigDecimal.ZERO:count.getExtrasAmount();
            BigDecimal discountAmount = count.getDiscountAmount()==null?BigDecimal.ZERO:count.getDiscountAmount();
            BigDecimal countAmount = rentAmount.add(depositAmount).add(extrasAmount).subtract(discountAmount);
            count.setCountAmount(countAmount);
        }
        return count;
    }

    /**
     * 查询未支付账单
     */
    @Override
    public void unpaidOrder(LocalDate evictionTime, List<Long> contractIds, List<String> orderType) {
        LambdaQueryWrapper<RentOrderDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(RentOrderDO::getBusinessId, contractIds)
                .in(RentOrderDO::getOrderType, orderType)
                .in(RentOrderDO::getPayStatus, Arrays.asList(OrderPayStatusEnum.UNPAID.getCode(),
                        OrderPayStatusEnum.PART.getCode()))
                .in(RentOrderDO::getApprovedStatus, Arrays.asList(OrderApprovedStatusEnum.HAVE_TAKEN_EFFECT.getCode()
                        , OrderApprovedStatusEnum.NO_AUDIT_REQUITED.getCode()))
                .le(RentOrderDO::getStartTime, evictionTime);
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(rentOrderDOList)) {
            throw exception0(NOT_PAY.getCode(), "{}" + NOT_PAY.getMsg(),
                    rentOrderDOList.stream().map(RentOrderDO::getRoomName).distinct().collect(Collectors.joining(",")));
        }
        LambdaQueryWrapper<RentOrderDO> qw = new LambdaQueryWrapper<>();
        qw.in(RentOrderDO::getBusinessId, contractIds)
                .in(RentOrderDO::getOrderType, orderType)
                .eq(RentOrderDO::getPayStatus, OrderPayStatusEnum.PROCESS.getCode())
//                .in(RentOrderDO::getApprovedStatus, Arrays.asList(OrderApprovedStatusEnum.HAVE_TAKEN_EFFECT.getCode
//                (), OrderApprovedStatusEnum.NO_AUDIT_REQUITED.getCode()))
//                .le(RentOrderDO::getStartTime, evictionTime)
        ;
        List<RentOrderDO> rentOrderDOS = rentOrderMapper.selectList(qw);
        if (CollectionUtils.isNotEmpty(rentOrderDOS)) {
            throw exception0(NOT_PAY.getCode(), "{}存在未处理账单，请联系财务勾核",
                    rentOrderDOS.stream().map(RentOrderDO::getRoomName).distinct().collect(Collectors.joining(",")));
        }
    }

    @Override
    public String orderCollection(List<Long> ids) {
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectBatchIds(ids);
        return orderReminder(rentOrderDOList);
    }

    @Override
    public String orderReminder(List<RentOrderDO> rentOrderDOList) {
        StringBuilder msg = new StringBuilder();
        List<RentOrderDO> successId = new ArrayList<>(rentOrderDOList.size());
        for (RentOrderDO rentOrderDO : rentOrderDOList) {
            SendUrgeMessageDTO messageDTO = new SendUrgeMessageDTO();
            messageDTO.setPhone(rentOrderDO.getPhone());
            messageDTO.setBegin(rentOrderDO.getStartTime().toString());
            messageDTO.setEnd(rentOrderDO.getEndTime().toString());
            messageDTO.setApartmentName(rentOrderDO.getApartmentName());
            messageDTO.setRoomName(rentOrderDO.getRoomName());
            messageDTO.setMemberId(rentOrderDO.getMemberId());
            messageDTO.setAmount(rentOrderDO.getTotalAmount().subtract(rentOrderDO.getPaidRentUtilities()));
            if (sendMessage(messageDTO, 0, msg)) {
                successId.add(rentOrderDO);
            }
        }
        if (CollectionUtils.isNotEmpty(successId)) {
            rentOrderMapper.updateBatch(successId.stream()
                    .map(id -> RentOrderDO.builder().id(id.getId())
                            .remindTimes((id.getRemindTimes() == null ? 1 : id.getRemindTimes() + 1))
                            .lastRemindTimes(LocalDateTime.now()).build())
                    .collect(Collectors.toList()), 100);
        }
        if (msg.length() == 0) {
            return ApiConstants.ORDER_COLLECTION_SUCCESS;
        }
        return msg.toString();
    }

    private boolean sendMessage(SendUrgeMessageDTO messageDTO, Integer type, StringBuilder msg) {
        SmsSendSingleToUserReqDTO sms = new SmsSendSingleToUserReqDTO();
        sms.setUserId(messageDTO.getMemberId());
        sms.setMobile(messageDTO.getPhone());
        //【承寓租赁】您的租金账单{begin}至{end}已生成，{roomName}，总金额{amount}元，请及时缴费。
        Map<String, Object> templateParams = new HashMap<>();
        if (0 == type) {
            // 租客催收短信
            sms.setTemplateCode("order_sms_collection");
            templateParams.put("apartmentName", messageDTO.getApartmentName());
            templateParams.put("roomName", messageDTO.getRoomName());
        } else {
            // 企业催收短信
            sms.setTemplateCode("company_order_sms_collection");
        }
        templateParams.put("begin", messageDTO.getBegin());
        templateParams.put("end", messageDTO.getEnd());
        templateParams.put("amount", messageDTO.getAmount());
        sms.setTemplateParams(templateParams);
        if (messageDTO.getPhone() == null) {
            log.info("发送短信手机号为空:{}", JSONObject.toJSONString(sms));
            msg.append(JSONObject.toJSONString("发送短信手机号为空"));
            return false;
        }
        CommonResult<Long> longCommonResult = smsSendApi.sendSingleSmsToMember(sms);
        log.info("发送催收短信 {}/{}", messageDTO.getPhone(), longCommonResult.isSuccess());
        if (!longCommonResult.isSuccess()) {
            msg.append("手机号为");
            msg.append(messageDTO.getPhone());
            msg.append("发送失败");
            return false;
        }

        return true;
    }

    @Override
    public Boolean getFirstOrderPayStatus(Long contractId) {
        LambdaQueryWrapper<RentOrderDO> qw = new LambdaQueryWrapper<>();
        qw.eq(RentOrderDO::getNum, 1)
                .eq(RentOrderDO::getBusinessId, contractId)
                .eq(RentOrderDO::getOrderType, OrderTypeEnum.DEFAULT.getCode())
                .eq(RentOrderDO::getPayStatus, OrderPayStatusEnum.PAID.getCode());
        Long aLong = rentOrderMapper.selectCount(qw);
        return aLong != 0;
    }

    /**
     * 账单不存在
     */
    private void validateContractRentOrderExists(Long id) {
        if (rentOrderMapper.selectById(id) == null) {
            throw exception(CONTRACT_RENTORDER_NOT_EXISTS);
        }
    }


    @Override
    public RentOrderInfoVO getCompanyOrders(Long companyContractId) {
        if (Objects.isNull(companyContractId)) {
            return null;
        }
        // 根据企业合同查询所有子合同账单信息 （合同账单和杂费账单）
        RentOrderQueryDTO queryDTO = new RentOrderQueryDTO();
        queryDTO.setCompanyContractId(companyContractId);
        queryDTO.setOrderTypeList(Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode(),
                OrderTypeEnum.EXTENSION.getCode(),OrderTypeEnum.METER.getCode()));
        queryDTO.setPayStatusList(Arrays.asList(OrderPayStatusEnum.UNPAID.getCode(), OrderPayStatusEnum.PART.getCode(),OrderPayStatusEnum.PAID.getCode()));
        List<RentOrderDO> companyOrderList = rentOrderMapper.getOrderListByCompanyContractId(queryDTO);
        if (CollectionUtils.isEmpty(companyOrderList)) {
            return null;
        }
        // 根据账单开始时间分组 周期企业合同账单
        Map<LocalDate, List<RentOrderDO>> cycleGroup = companyOrderList.stream().collect(Collectors.groupingBy(RentOrderDO::getStartTime));
        List<RentOrderDO> companyOrderSummaryList = new ArrayList<>();
        for (List<RentOrderDO> orderList : cycleGroup.values()) {
            // 各个周期的所有子合同账单 （合同账单 & 杂费账单）
            // 合同账单
            RentOrderDO contractRentOrderDO = null;
            BigDecimal contractRentOrderAmount = BigDecimal.ZERO;
            BigDecimal contractRentOrderPaid = BigDecimal.ZERO;
            List<Long> contractCompanyOrderIds = new ArrayList<>();
            // 杂费账单
            RentOrderDO customRentOrderDO = null;
            BigDecimal customRentOrderAmount = BigDecimal.ZERO;
            BigDecimal customRentOrderPaid = BigDecimal.ZERO;
            List<Long> customCompanyOrderIds = new ArrayList<>();
            for (RentOrderDO orderDO : orderList) {
                if (OrderTypeEnum.DEFAULT.getCode().equals(orderDO.getOrderType())) {
                    // 合同账单
                    if (Objects.isNull(contractRentOrderDO)) {
                        contractRentOrderDO = new RentOrderDO();
                    }
                    contractRentOrderDO.setStartTime(orderDO.getStartTime());
                    contractRentOrderDO.setEndTime(orderDO.getEndTime());
                    contractRentOrderDO.setOughtPayTime(orderDO.getOughtPayTime());
                    contractRentOrderDO.setOrderType(orderDO.getOrderType());
                    contractRentOrderAmount = contractRentOrderAmount.add(orderDO.getTotalAmount());
                    contractRentOrderPaid = contractRentOrderPaid.add(orderDO.getPaidRentUtilities());
                    if (OrderPayStatusEnum.UNPAID.getCode().equals(orderDO.getPayStatus()) || OrderPayStatusEnum.PART.getCode().equals(orderDO.getPayStatus())) {
                        contractCompanyOrderIds.add(orderDO.getId());
                    }
                } else {
                    // 杂费账单
                    if (Objects.isNull(customRentOrderDO)) {
                        customRentOrderDO = new RentOrderDO();
                    }
                    customRentOrderDO.setStartTime(orderDO.getStartTime());
                    customRentOrderDO.setEndTime(orderDO.getEndTime());
                    customRentOrderDO.setOughtPayTime(orderDO.getOughtPayTime());
                    customRentOrderDO.setOrderType(orderDO.getOrderType());
                    customRentOrderAmount = customRentOrderAmount.add(orderDO.getTotalAmount());
                    customRentOrderPaid = customRentOrderPaid.add(orderDO.getPaidRentUtilities());
                    if (OrderPayStatusEnum.UNPAID.getCode().equals(orderDO.getPayStatus()) || OrderPayStatusEnum.PART.getCode().equals(orderDO.getPayStatus())) {
                        customCompanyOrderIds.add(orderDO.getId());
                    }
                }
            }
            if (Objects.nonNull(contractRentOrderDO)) {
                buildCycleRentOrder(contractRentOrderDO, contractRentOrderAmount, contractRentOrderPaid, contractCompanyOrderIds);
                companyOrderSummaryList.add(contractRentOrderDO);
            }
            if (Objects.nonNull(customRentOrderDO)) {
                buildCycleRentOrder(customRentOrderDO, customRentOrderAmount, customRentOrderPaid, customCompanyOrderIds);
                companyOrderSummaryList.add(customRentOrderDO);
            }
        }
        return buildRentOrderInfoVO(companyOrderSummaryList);
    }

    private void buildCycleRentOrder(RentOrderDO rentOrderDO, BigDecimal totalAmount, BigDecimal paidAmount, List<Long> companyOrderIds) {
        rentOrderDO.setTotalAmount(totalAmount);
        rentOrderDO.setPaidRentUtilities(paidAmount);
        rentOrderDO.setCompanyOrderIds(companyOrderIds);

        int compare = rentOrderDO.getTotalAmount().compareTo(rentOrderDO.getPaidRentUtilities());
        if (compare == ZERO) {
            // 全部支付
            rentOrderDO.setPayStatus(OrderPayStatusEnum.PAID.getCode());
        } else {
            // 部分支付
            rentOrderDO.setPayStatus(OrderPayStatusEnum.PART.getCode());
        }
        if (BigDecimal.ZERO.compareTo(rentOrderDO.getPaidRentUtilities()) == ZERO) {
            // 已收账单总额为0
            rentOrderDO.setPayStatus(OrderPayStatusEnum.UNPAID.getCode());
        }
    }

    @Override
    public String getCompanyBatchNo(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return "";
        }
        // 生成企业账单批次号
        String companyOrderBatchNo = UUID.randomUUID().toString(true);
        // 给子账单设置批次号
        LambdaUpdateWrapper<RentOrderDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(RentOrderDO::getId, ids);
        updateWrapper.set(RentOrderDO::getCompanyOrderBatchNo, companyOrderBatchNo);
        rentOrderMapper.update(null, updateWrapper);
        return companyOrderBatchNo;
    }


    @Override
    public List<RentOrderDO> getRentListByBatchNo(String companyBatchNo) {
        if (StringUtils.isBlank(companyBatchNo)) {
            return null;
        }
        return rentOrderMapper.selectList(RentOrderDO::getCompanyOrderBatchNo, companyBatchNo);
    }


    @Override
    public String urgeCompanyPayOrder(CompanyOrderUrgeReqVO reqVO) {
        CompanyContractRentDataRespVO companyContractResp = companyContractService.getByCompanyContractId(reqVO.getCompanyContractId());
        if (Objects.isNull(companyContractResp)) {
            throw exception(COMPANY_CONTRACT_NOT_EXISTS);
        }
        RentOrderQueryDTO queryDTO = new RentOrderQueryDTO();
        queryDTO.setCompanyContractId(reqVO.getCompanyContractId());
        queryDTO.setStartTime(reqVO.getStartTime());
        queryDTO.setEndTime(reqVO.getEndTime());
        queryDTO.setOrderType(reqVO.getOrderType());
        List<RentOrderDO> companyOrderList = rentOrderMapper.getOrderListByCompanyContractId(queryDTO);
        BigDecimal rentOrderAmount = BigDecimal.ZERO;
        BigDecimal rentOrderPaid = BigDecimal.ZERO;
        for (RentOrderDO rentOrderDO : companyOrderList) {
            rentOrderAmount = rentOrderAmount.add(rentOrderDO.getTotalAmount());
            rentOrderPaid = rentOrderPaid.add(rentOrderDO.getPaidRentUtilities());
        }
        // 待付款金额
        BigDecimal amount = rentOrderAmount.subtract(rentOrderPaid);
        SendUrgeMessageDTO sendMsgDto = new SendUrgeMessageDTO();
        sendMsgDto.setPhone(companyContractResp.getContactsPhone());
        sendMsgDto.setBegin(reqVO.getStartTime().format(DateTimeFormatter.ofPattern(DateUtils.FORMAT_DATE)));
        sendMsgDto.setEnd(reqVO.getEndTime().format(DateTimeFormatter.ofPattern(DateUtils.FORMAT_DATE)));
        sendMsgDto.setAmount(amount);
        StringBuilder msg = new StringBuilder();
        sendMessage(sendMsgDto, 1, msg);
        if (StringUtils.isBlank(msg)) {
            return ApiConstants.ORDER_COLLECTION_SUCCESS;
        }
        return String.valueOf(msg);
    }

    @Override
    public List<OrderDetaillAndOrderFeeVo> getInvoiceOrder(Long loginUserId, Integer invoiceStatus) {

        log.info("loginUserId:{},invoiceStatus：{},payStatus{}", loginUserId);
        //账单
        List<OrderDetaillAndOrderFeeVo> rentOrderRespVOList = rentOrderMapper.getInvoiceOrder(loginUserId, invoiceStatus);

        return rentOrderRespVOList;
    }

    /**
     * 检查账单支付结果(防止支付回调不成功)
     *
     * @param rentOrderDOList
     */
    private void checkPaidResult(List<RentOrderDO> rentOrderDOList) {
        List<RentOrderDO> unpaid = rentOrderDOList.stream().filter(o -> !OrderPayStatusEnum.PAID.getCode().equals(o.getPayStatus()))
                .collect(Collectors.toList());
        try {
            if (CollectionUtils.isNotEmpty(unpaid)) {
                CommonResult<Map<String, BigDecimal>> checkPaidAmount = financeApi.checkPayResult(CheckPayResultReqDTO.builder()
                        .sourceBizType(SourceBizTypeEnum.RENT_ORDER.name())
                        .sourceBizId(unpaid.stream().map(o -> o.getId().toString()).collect(Collectors.toSet()))
                        .build());
                if (checkPaidAmount.isSuccess()) {
                    Map<String, BigDecimal> m = checkPaidAmount.getData();
                    rentOrderDOList.forEach(o -> {
                        if (m.containsKey(o.getId().toString()) && m.get(o.getId().toString()).compareTo(o.getTotalAmount()) >= 0) {
                            o.setPayStatus(OrderPayStatusEnum.PAID.getCode());
                        }
                    });
                }
            }
        } catch (Exception ignore) {
        }
    }

    @Override
    public List<RentOrderDO> getRentOrderByBusinessId(Long contractId) {
        //获取合同下的所有账单
        LambdaQueryWrapper<RentOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RentOrderDO::getBusinessId, contractId)
                .in(RentOrderDO::getPayStatus, Arrays.asList(OrderPayStatusEnum.UNPAID.getCode(), OrderPayStatusEnum.PART.getCode()))
                .in(RentOrderDO::getOrderType, Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode()));
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(wrapper);

        return rentOrderDOList;
    }

    /*
     * 租客账单待处理、未出账、已处理
     * */
    @Override
    public RentOrderInfoVO getRentOrderByRoomId(Long roomId, List<String> orderTypes) {
        //获取合同房间下的所有账单
        LambdaQueryWrapper<RentOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RentOrderDO::getRoomId, roomId)
                .in(CollectionUtils.isNotEmpty(orderTypes), RentOrderDO::getOrderType, orderTypes);
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(wrapper);

        return buildRentOrderInfoVO(rentOrderDOList);
    }

    @Override
    public void createAppRentOrder(AppRentOrderCreateReq reqVo) {
        RentOrderCreateReq param = RentOrderConvert.INSTANCE.convertAppReq(reqVo);
        //根据房间id查询当前时间已生效的合同
        LambdaQueryWrapper<ContractRoomDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ContractRoomDO::getRoomId, reqVo.getRoomId())
                .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractStatusEnum.ACTIVE.getCode(), ContractStatusEnum.CHECK_IN.getCode()))
                .ge(ContractRoomDO::getEndTime, LocalDate.now())
                .le(ContractRoomDO::getStartTime, LocalDate.now());
        ContractRoomDO contractRoomDO = contractRoomMapper.selectOne(lambdaQueryWrapper);
        if (contractRoomDO == null) {
            throw exception(ROOM_NO_CONTRACT);
        }
        param.setContractId(contractRoomDO.getId());
        param.setApartmentId(contractRoomDO.getApartmentId());
        param.setApartmentName(contractRoomDO.getApartmentName());
        param.setRoomName(contractRoomDO.getRoomName());
        createRentOrder(param);
    }

    /**
     * 合同ids  查询 过期未支付的账单
     */
    @Override
    public List<RentOrderDO> overdueUnpaidOrder(List<Long> contractIds) {
        LambdaQueryWrapper<RentOrderDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(RentOrderDO::getBusinessId, contractIds)
                .in(RentOrderDO::getOrderType, Arrays.asList(OrderTypeEnum.DEFAULT.getCode()))
                .in(RentOrderDO::getPayStatus, Arrays.asList(OrderPayStatusEnum.UNPAID.getCode(), OrderPayStatusEnum.PART.getCode()))
                .le(RentOrderDO::getDeadlinePayTime, LocalDateTime.now());
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(queryWrapper);
        return rentOrderDOList;
    }

    @Override
    public void orderApproval(ApproveReqVO reqVO) {
        RentOrderDO rentOrderDO = new RentOrderDO();
        rentOrderDO.setId(reqVO.getId());
        //账单审批通过
        if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(reqVO.getResult())) {
            rentOrderDO.setApprovedStatus(OrderApprovedStatusEnum.HAVE_TAKEN_EFFECT.getCode());

        }
        if (BpmProcessInstanceResultEnum.REJECT.getResult().equals(reqVO.getResult())) {
            rentOrderDO.setApprovedStatus(OrderApprovedStatusEnum.REJECT.getCode());

        }
        rentOrderMapper.updateById(rentOrderDO);
        log.info("账单审批状态修改:{}", JSONObject.toJSONString(rentOrderDO));
    }

    @Override
    public  List<RentOrderContractVO> getContractOrder(Long id) {
        LambdaQueryWrapper<RentOrderDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(RentOrderDO::getBusinessId, id)
                .in(RentOrderDO::getOrderType, Arrays.asList(OrderTypeEnum.DEFAULT.getCode()))
        ;
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(queryWrapper);

        return  RentOrderConvert.INSTANCE.convertVo(rentOrderDOList);
    }

    @Override
    public void updateById(RentOrderDO update) {
        rentOrderMapper.updateById(update);
    }

    @Override
    public PageResult<RentOrderDO> getReminderOrder(PageParam page) {
        IPage<T> mpPage = MyBatisUtils.buildPage(page);
        IPage<RentOrderDO> rentOrderPage = rentOrderMapper.selectReminderOrder(mpPage);
        PageResult<RentOrderDO> respPage = new PageResult<>();
        respPage.setTotal(rentOrderPage.getTotal());
        respPage.setList(rentOrderPage.getRecords());
        return respPage;
    }

    @Override
    public List<RentOrderExcelVO> exportOrder(RentOrderQueryVo req) {
        Long tenantId = Objects.requireNonNull(SecurityFrameworkUtils.getLoginUser()).getTenantId();
        List<RentOrderEXDO> rentOrder = rentOrderMapper.getRentOrder(req, tenantId);
        List<RentOrderResp> rentOrderRespVO = RentOrderConvert.INSTANCE.convertList01(rentOrder);
        for (RentOrderResp item : rentOrderRespVO) {
            if (StringUtils.isBlank(req.getOrderStatus())) {
                setOrderStatus(item);
            } else {
                item.setOrderStatus(req.getOrderStatus());
                String msg = OrderStatusEnum.getEnum(req.getOrderStatus()).getMsg();
                item.setOrderStatusName(msg);
                if(OrderStatusEnum.OVERDUE.getCode().equals(req.getOrderStatus())){
                    long days = ChronoUnit.DAYS.between(item.getOughtPayTime(), LocalDate.now());
                    item.setBillTypeName("已逾期"+days+"天");
                }
            }
            if (OrderTypeEnum.getName(item.getOrderType())!=null) {
                item.setOrderType(OrderTypeEnum.getName(item.getOrderType()));
            }

        }
        List<RentOrderExcelVO> res = RentOrderConvert.INSTANCE.convertList05(rentOrderRespVO);

        return res;
    }

    @Override
    public List<RentOrderSowTicketVO> getOrderBySowTicket(Long id) {
        LambdaQueryWrapper<RentOrderDO> qw = new LambdaQueryWrapper<>();
        qw.eq(RentOrderDO::getSowTicketId,id)
                .orderByDesc(RentOrderDO::getCreateTime);
        return RentOrderConvert.INSTANCE.convertList06(rentOrderMapper.selectList(qw));
    }

    @Override
    public RentOrderDetailVO getOrderDetail(Long id) {
        // 获取账单
        RentOrderDO rentOrderDO = rentOrderMapper.selectById(id);
        RentOrderDetailVO rentOrderDetailVO = new RentOrderDetailVO();
        BeanUtils.copyProperties(rentOrderDO,rentOrderDetailVO);
        //查询账单明细
        List<OrderFeesDO> orderFeesDOS = orderfeesMapper.selectList(OrderFeesDO::getOrderId,rentOrderDO.getId());
        if (CollectionUtils.isNotEmpty(orderFeesDOS)){
            List<OrderFeesInformationVO> list = new ArrayList<>();
            orderFeesDOS.forEach(o->{
                OrderFeesInformationVO orderFeesInformationVO = new OrderFeesInformationVO();
                BeanUtils.copyProperties(o,orderFeesInformationVO);
                orderFeesInformationVO.setBooking(o.getBookingAmount());
                //应收=总金额-优惠金额
                orderFeesInformationVO.setReceivableAmount(orderFeesInformationVO.getAmount()
                        .subtract(orderFeesInformationVO.getDiscountAmount() == null ? BigDecimal.ZERO:orderFeesInformationVO.getDiscountAmount()));
                /*//已收 =+ 优惠金额
                orderFeesInformationVO.setPaidAmount((orderFeesInformationVO.getPaidAmount() == null ?  BigDecimal.ZERO:orderFeesInformationVO.getPaidAmount())
                        .add(orderFeesInformationVO.getDiscountAmount() == null ? BigDecimal.ZERO:orderFeesInformationVO.getDiscountAmount()));
*/
                //未收金额 = 应收-已收
                orderFeesInformationVO.setNoPaidAmount(orderFeesInformationVO.getReceivableAmount()
                        .subtract(orderFeesInformationVO.getPaidAmount() == null ? BigDecimal.ZERO :
                                orderFeesInformationVO.getPaidAmount()));
                list.add(orderFeesInformationVO);
            });
            rentOrderDetailVO.setCostInformation(list);
        }
        return rentOrderDetailVO;
    }

    @Override
    public int update(Wrapper<RentOrderDO> updateWrapper) {
        return rentOrderMapper.update(null, updateWrapper);
    }

    @Override
    public void updateBatch(List<RentOrderDO> list) {
        if (CollectionUtil.isNotEmpty(list)) {
            rentOrderMapper.updateBatch(list, list.size());
        }

    }


    @Override
    public void overdueOrderDeal(RentOrderDO rentOrderDO) {
        // 判断是否还有逾期账单
        LambdaQueryWrapper<RentOrderDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RentOrderDO::getBusinessId, rentOrderDO.getBusinessId());
        queryWrapper.eq(RentOrderDO::getOrderType, OrderTypeEnum.DEFAULT.getCode());
        queryWrapper.in(RentOrderDO::getPayStatus, OrderPayStatusEnum.UNPAID.getCode(),OrderPayStatusEnum.PART.getCode());
        queryWrapper.le(RentOrderDO::getStartTime, LocalDate.now());
        Long aLong = rentOrderMapper.selectCount(queryWrapper);
        if (Objects.nonNull(aLong) && aLong == 0){
            // 无逾期的账单了  恢复水电
            log.info("逾期账单支付完成，恢复水电：{}", JSONObject.toJSONString(rentOrderDO));
            try {
                ResultModel<Boolean> booleanResultModel = iotRoomDeviceApi.setWaterMeterAndAmmeterSwitchOn(rentOrderDO.getRoomId());
                log.info("逾期账单支付完成，恢复水电，返回参数：{}", booleanResultModel.getData());
            } catch (Exception e) {
                log.error("逾期账单支付完成，恢复水电异常：", e);
            }
        }
    }

    /**
     * 根据工单id列表获取对应的维修工单账单信息
     * @param getSowTicketOrderByIdsReq
     * @return
     */
    @Override
    public Map<Long, List<RentOrderSowTicketVO>> getSowTicketOrderByIds(GetSowTicketOrderByIdsReq getSowTicketOrderByIdsReq) {
        LambdaQueryWrapper<RentOrderDO> qw = new LambdaQueryWrapper<>();
        qw.in(RentOrderDO::getSowTicketId, getSowTicketOrderByIdsReq.getSwoTicketIds())
                .orderByDesc(RentOrderDO::getCreateTime);
        List<RentOrderSowTicketVO> rentOrderSowTicketVOS = RentOrderConvert.INSTANCE.convertList06(rentOrderMapper.selectList(qw));
        Map<Long, List<RentOrderSowTicketVO>> map = rentOrderSowTicketVOS.stream().collect(Collectors.groupingBy(RentOrderSowTicketVO::getSowTicketId));
        return map;
    }

    /**
     * 导入能耗账单
     * @param list
     */
    @Override
    public ContractRoommateImportRespVO energyConsumptionImport(List<EnergyConsumptionImportExcelVO> list) {
        List<String> addList = new ArrayList<>();
        Map<String, String> failureUsernames = new HashMap<>();

        list.forEach(item->{
            RentOrderDO rentOrderDO = new RentOrderDO();
            if(!item.getBillTypeName().equals("待处理") && !item.getBillTypeName().equals("应处理") && !item.getBillTypeName().equals("逾期")){
                failureUsernames.put(item.getRoomName(),"只能导入待处理账单");
                return;
            }
            ApartmentListDto apartmentListDto = apartmentApi.queryApartmentByName(item.getApartmentName()).getCheckedData();
            if(apartmentListDto != null){
                rentOrderDO.setApartmentId(apartmentListDto.getId());
                //根据房源信息查询房源
                RoomHouseRespVo roomHouseRespVo = roomHouseApi.queryByAddr(item.getRoomName(), apartmentListDto.getId()).getCheckedData();
                if(roomHouseRespVo != null){
                    //查询对应合同
                    LambdaQueryWrapper<ContractRoomDO> wrapper = Wrappers.lambdaQuery();
                    wrapper.eq(ContractRoomDO::getRoomId, roomHouseRespVo.getId());
                    wrapper.eq(ContractRoomDO::getApartmentId, roomHouseRespVo.getApartmentId());
                    wrapper.eq(ContractRoomDO::getContractStatus, "active");
                    wrapper.eq(ContractRoomDO::getDeleted, 0);
                    //查询租客合同表
                    ContractRoomDO contractRoomDO = contractRoomMapper.selectOne(wrapper);
                    if(contractRoomDO == null){
                        //企业账单
                        CompanyContractListRespVO companyContractListRespVO = companyContractMapper.queryByRoomId(roomHouseRespVo.getId());
                        if(companyContractListRespVO != null){
                            BeanUtil.copyProperties(companyContractListRespVO,rentOrderDO);
                            rentOrderDO.setBusinessId(companyContractListRespVO.getCompanyContractId());
                            rentOrderDO.setStartTime(companyContractListRespVO.getStartTime());
                            rentOrderDO.setEndTime(companyContractListRespVO.getEndTime());
                            rentOrderDO.setPayer(CompanyContractOrderShareTypeEnum.getMsgByCode(companyContractListRespVO.getOrderShareMethod()));
                        }else {
                            failureUsernames.put(item.getRoomName(),"该房源不存在生效中合同");
                            return;
                        }
                    }else{
                        BeanUtil.copyProperties(contractRoomDO,rentOrderDO);
                        rentOrderDO.setBusinessId(contractRoomDO.getId());
                        rentOrderDO.setStartTime(contractRoomDO.getStartTime());
                        rentOrderDO.setEndTime(contractRoomDO.getEndTime());
                        rentOrderDO.setPayer(contractRoomDO.getCustomerName());
                    }

                    //参数
                    //个人承担 0 | 企业承担1
                    rentOrderDO.setRoommateId(item.getRoommateId().equals("个人")?0:1);

                    BeanUtils.copyProperties(item,rentOrderDO);
                    //账单类型为抄表账单
                    rentOrderDO.setOrderType("meter");
                    rentOrderDO.setApprovedStatus("notReview");

                    rentOrderDO.setPayStatus("unpaid");

                    rentOrderDO.setOughtPayTime(DateUtil.date().toSqlDate().toLocalDate());
                    //最晚支付时间discountAmount = null
                    rentOrderDO.setDeadlinePayTime(DateUtil.offsetDay(new Date(),10).toSqlDate().toLocalDate());
                    //计算金额
                    rentOrderDO.setPaidRentUtilities(item.getTotalAmount().subtract(item.getReceivableAmount()));
                    rentOrderDO.setActualPaymentAmount(item.getTotalAmount().subtract(item.getReceivableAmount()));
                    //结算
                    rentOrderDO.setRemindTimes(0);
                    rentOrderDO.setFlowType(0);

                    rentOrderDO.setOrderNo(nextRentOrderNo());
                    rentOrderDO.setId(null);
                    rentOrderMapper.insert(rentOrderDO);

                    //查询账单
                    LambdaQueryWrapper<RentOrderDO> wrapperRentOrder = Wrappers.lambdaQuery();
                    wrapperRentOrder.eq(RentOrderDO::getOrderNo, rentOrderDO.getOrderNo());
                    rentOrderDO = rentOrderMapper.selectOne(wrapperRentOrder);
                    //增加账单明细
                    OrderFeesDO orderFeesDO = new OrderFeesDO();
                    BeanUtil.copyProperties(rentOrderDO,orderFeesDO);
                    orderFeesDO.setOrderId(rentOrderDO.getId());
                    orderFeesDO.setAmount(item.getTotalAmount());
                    orderFeesDO.setPaidAmount(item.getTotalAmount().subtract(item.getReceivableAmount()));
                    orderFeesDO.setDiscountAmount(new BigDecimal(0));
                    orderFeesDO.setFeeMonth(new BigDecimal(0));
                    orderFeesDO.setFeeId(6L);
                    orderFeesDO.setFeeName("电费");
                    orderFeesDO.setFeeSort("dianfei_fees");
                    orderFeesDO.setId(null);
                    orderfeesMapper.insert(orderFeesDO);
                    addList.add(item.getOrderNo());
                    return;
                }
                failureUsernames.put(item.getRoomName(),"未查询到对应房源信息");
            }
            failureUsernames.put(item.getRoomName(),"未查询到对应公寓");

        });
        ContractRoommateImportRespVO build = ContractRoommateImportRespVO.builder().createUsernames(addList).failureUsernames(failureUsernames).build();
        return build;
    }

    @Override
    public JSONObject templateDownload(Long apartmentId) {
        String fileName = "能耗模板.xlsx";
        List<EnergyConsumptionImportExcelVO> list = new ArrayList<>();
        ExcelWriter excelWriter = EasyExcel.write(fileName, EnergyConsumptionImportExcelVO.class).build();
        WriteSheet sheet = EasyExcel.writerSheet("sheet1").head(EnergyConsumptionImportExcelVO.class).build();
        excelWriter.write(list, sheet);
        excelWriter.finish();
        String url = oosFileService.oosUploadFile(new File(fileName));
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("url",url);
        jsonObject.put("fileName",fileName);
        return jsonObject;
    }

    /**
     * 查询时间段的账单
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public List<RentOrderDO> getPaymentRentOrderList(String startTime, String endTime) {
        return rentOrderMapper.getPaymentRentOrderList(startTime,endTime);
    }
}
